package com.example.mattrigle;

import java.io.File;
import java.io.IOException;

import com.example.mattrigle.GameCanvas.InteractListener;
import com.example.mattrigle.LineBar.SeekBarListener;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.Paint.Style;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class AnimationGameCanvas extends View implements SeekBarListener {

	// ==========================================================
	// Delegate & Callback
	// ==========================================================
	public abstract interface InteractListener {
		public void onBeginDrag(LineBar obj, float value,long currentTime,Point position);

		public void onValueChange(LineBar obj, float value,long currentTime,Point position);

		public void onEndDrag(LineBar obj, float value,long currentTime,long elapsedTime,Point position);
	}

	// ==========================================================
	// Constants
	// ==========================================================
	private int TRACK_HEIGHT = 50;

	private class GameCanvasOption
	{
		public  boolean isViewForAnimate = false;
		public int align = ALIGN_CENTER;
		public int valign = VALIGN_MIDDLE;
		public int pos_hand = POS_HAND_SLIDE;
		public Point position = new Point(0, 0);
		public boolean isShowAngle = true;
		public boolean isShowSeekBar = true;
		public boolean isLock = false;
		public float thumb_length = 1f;
		public float scale = 0.5f;
		public int num_float_bit = 1;
		public boolean isEnableCacheValue = true;
		public int mode = MODE_DISPLAY;
		public boolean isHideCorner = true;
		public boolean multiCorner = false;
		public boolean isHideHand = true;
		public boolean isMultiActive = false;
		public boolean isCosActive = true;
		public boolean isHideArc = false;
		public boolean isHideLabel = false;
		public boolean isFlipX = false;
		
		public GameCanvasOption clone()
		{
			GameCanvasOption tmp = new GameCanvasOption();
			tmp.isViewForAnimate = isViewForAnimate;
			tmp.align = align;
			tmp.isViewForAnimate = isViewForAnimate;
			tmp.align = align;
			tmp.valign = valign;
			tmp.position = position;
			tmp.multiCorner = multiCorner;
			tmp.isShowAngle = isShowAngle;
			tmp.isShowSeekBar = isShowSeekBar;
			tmp.isLock = isLock;
			tmp.thumb_length = thumb_length;
			tmp.isHideArc = isHideArc;
			tmp.scale = scale;
			tmp.num_float_bit = num_float_bit;
			tmp.isEnableCacheValue = isEnableCacheValue;
			tmp.mode = mode;
			tmp.isHideCorner = isHideCorner;
			tmp.isHideHand = isHideHand;
			tmp.isMultiActive = isMultiActive;
			tmp.isCosActive = isCosActive;
			tmp.isHideLabel = isHideLabel;
			return tmp;
		}
	}
	
	// ==========================================================
	// Field
	// ==========================================================
	private Paint tanPaint = new Paint();
	private Paint anglePaint = new Paint();
	private Paint markAnglePaint = new Paint();
	private CornerDrawable corner_cos_anim;
	private CornerDrawable corner_sin_anim;

    private RectF cosRectF = null;
    private RectF sinRectF = null;
	private Point offset = new Point(0, 0);

	private int doc_width = 120, doc_height = 260;
	private int object_width = 120, object_height = 260;
	private float cosDegree = 0f;

	private LineBar cosSeekBar, sinSeekBar, tanSeekBar;
	private LineBar mCosSeekBar, mSinSeekBar;
	
	private GameCanvasOption option = new GameCanvasOption();
	
	// ==========================================================
	// Properties
	// ==========================================================
	public static final int ALIGN_CENTER = 0;
	public static final int ALIGN_LEFT = 1;
	public static final int ALIGN_RIGHT = 2;

	public int getAlign() {
		return this.option.align;
	}

	public void setAlign(int align) {
		switch (align) {
		case ALIGN_CENTER:
		case ALIGN_LEFT:
		case ALIGN_RIGHT:
			this.option.align = align;
			break;
		default:
			this.option.align = ALIGN_CENTER;
			break;
		}
		calPos();
	}

	public static final int VALIGN_MIDDLE = 0;
	public static final int VALIGN_TOP = 1;
	public static final int VALIGN_BOTTOM = 2;

	public int getValign() {
		return this.option.valign;
	}

	public void setValign(int valign) {
		switch (valign) {
		case ALIGN_CENTER:
		case ALIGN_LEFT:
		case ALIGN_RIGHT:
			this.option.valign = valign;
			break;
		default:
			this.option.valign = VALIGN_MIDDLE;
			break;
		}
		calPos();
	}

	public void setPosition(int x, int y) {
		this.option.position.x = x;
		this.option.position.y = y;
		calPos();
	}

	public Point getPosition() {
		return this.option.position;
	}

	public boolean IsShowAngle() {
		return this.option.isShowAngle;
	}

	public void setShowAngle(boolean isShowAngle) {
		this.option.isShowAngle = isShowAngle;
		this.invalidate();
	}

	public boolean IsShowSeekBar() {
		return this.option.isShowSeekBar;
	}

	public void setShowSeekBar(boolean isShowSeekBar) {
		this.option.isShowSeekBar = isShowSeekBar;
		this.invalidate();
	}

	public boolean getLockSeekBar() {
		return this.option.isLock;
	}

	public void LockSeekBar(boolean isLock) {
		this.option.isLock = isLock;
	}

	public float getMaxTanThumbnailLength() {
		return this.option.thumb_length;
	}

	public void setMaxTanThumbnailLength(float length) {
		this.option.thumb_length = length > 1f ? 1f : length<0.5f?0.5f:length;
		mCosSeekBar.setMaximumLength(this.option.thumb_length);
		mSinSeekBar.setMaximumLength(this.option.thumb_length);
	}

	public float getScale() {
		return this.option.scale;
	}

	public void setScale(float scale) {
		this.option.scale = scale < 0.1f ?  0.1f : scale>0.6f?0.6f:scale;
		this.sinSeekBar.setScale(scale);
		this.cosSeekBar.setScale(scale);
		this.tanSeekBar.setScale(scale);
		calSize();
	}

	public int getNumOfFloatBit() {
		return this.option.num_float_bit;
	}

	public void setNumOfFloatBit(int num_float_bit) {
		this.option.num_float_bit = num_float_bit < 1 ? 1 : num_float_bit > 6 ? 6
				: num_float_bit;
		this.invalidate();
	}

	public boolean isEnableCacheValue() {
		return this.option.isEnableCacheValue;
	}

	public void setEnableCacheValue(boolean isEnableCacheValue) {
		this.option.isEnableCacheValue = isEnableCacheValue;
	}

	public boolean getMultiCorner()
	{
		return this.option.multiCorner;
	}

	public void setMultiCorner(boolean multiCorner) {
		this.option.multiCorner = multiCorner;
		this.invalidate();
	}

	public boolean IsHideCorner()
	{
		return this.option.isHideCorner;
	}

	public void setHideCorner(boolean isHideCorner) {
		this.option.isHideCorner = isHideCorner;

		if (corner_sin_anim == null) {
			try {
				corner_sin_anim = new CornerDrawable(this, (new File(CornerDrawable.getLocalPath(this.getContext())).exists())?null:getResources()
						.openRawResource(R.raw.cartoon1), true);

			} catch (NotFoundException e) {
				corner_sin_anim = null;
			} catch (IOException e) {
				corner_sin_anim = null;
			}
			if(corner_sin_anim!=null)corner_sin_anim.setForeground(new Rect(mSinSeekBar.getCursorX() - 40,
					mSinSeekBar.getCursorY() - 60,
					mSinSeekBar.getCursorX() + 60,
					mSinSeekBar.getCursorY() + 40));
		}
		if (corner_cos_anim == null) {
			try {
				corner_cos_anim = new CornerDrawable(this, (new File(CornerDrawable.getLocalPath(this.getContext())).exists())?null:getResources()
						.openRawResource(R.raw.cartoon1), true);

			} catch (NotFoundException e) {
				corner_cos_anim = null;
			} catch (IOException e) {
				corner_cos_anim = null;
			}

			if(corner_cos_anim!=null)corner_cos_anim.setForeground(new Rect(mCosSeekBar.getCursorX() - 40,
					mCosSeekBar.getCursorY() - 60,
					mCosSeekBar.getCursorX() + 60,
					mCosSeekBar.getCursorY() + 40));

		}
		if(!this.option.isHideCorner)
		{
			corner_cos_anim.start();
			corner_sin_anim.start();
		}
		else
		{
			corner_cos_anim.stop();
			corner_sin_anim.stop();
			this.invalidate();
		}
	}

	public static final int POS_HAND_SEEKBAR = 0;
	public static final int POS_HAND_SLIDE = 1;
	public static final int POS_HAND_BOTH = 2;

	public int getPosHand()
	{
		return this.option.pos_hand;
	}
	
	public void setPosHand(int pos_hand) {
		this.option.pos_hand = (pos_hand==POS_HAND_SEEKBAR||pos_hand==POS_HAND_SLIDE||pos_hand==POS_HAND_BOTH)?pos_hand:POS_HAND_SLIDE;
		this.invalidate();
	}

	public boolean IsHideLabel()
	{
		return this.option.isHideLabel;
	}
	
	public void setHideLabel(boolean isHideLabel) {
		this.option.isHideLabel = isHideLabel;
		
		this.sinSeekBar.setHideLabel(isHideLabel);
		this.mSinSeekBar.setHideLabel(isHideLabel);
		this.cosSeekBar.setHideLabel(isHideLabel);
		this.mCosSeekBar.setHideLabel(isHideLabel);
		this.invalidate();
	}

	public boolean IsHideHand()
	{
		return this.option.isHideHand;
	}
	
	public void setHideHand(boolean isHideHand) {
		this.option.isHideHand = isHideHand;
		
		this.sinSeekBar.setHideHand(isHideHand||this.option.isViewForAnimate||(this.option.isCosActive&&!this.option.isMultiActive));
		this.mSinSeekBar.setHideHand(isHideHand||(this.option.isCosActive&&!this.option.isMultiActive));
		this.cosSeekBar.setHideHand(isHideHand||this.option.isViewForAnimate||(!this.option.isCosActive&&!this.option.isMultiActive));
		this.mCosSeekBar.setHideHand(isHideHand||(!this.option.isCosActive&&!this.option.isMultiActive));
		this.invalidate();
	}

	public void setCosActive(boolean isCosActive) {
		this.option.isCosActive = isCosActive;
		
		this.sinSeekBar.setEnable(!isCosActive);
		this.sinSeekBar.setHideLabel(isCosActive);
		this.mSinSeekBar.setEnable(!isCosActive);
		
		this.cosSeekBar.setEnable(isCosActive);
		this.cosSeekBar.setHideLabel(!isCosActive);
		this.mCosSeekBar.setEnable(isCosActive);

		setHideHand(this.option.isHideHand);
	}

	private float line_width = 1f;

	public float getLineWidth() {
		return line_width * 2;
	}

	public void setLineWidth(int line_width) {
		this.line_width = (line_width < 1) ? 1 : line_width;
		this.tanPaint.setStrokeWidth(line_width);
		
		this.cosSeekBar.setLineWidth(line_width/3);
		this.sinSeekBar.setLineWidth(line_width/3);
		this.tanSeekBar.setLineWidth(line_width/3);
		this.mCosSeekBar.setLineWidth(line_width);
		this.mSinSeekBar.setLineWidth(line_width);
	}

	public boolean IsMultiActive()
	{
		return this.option.isMultiActive;
	}
	public void setMultiActive(boolean isMultiActive) {
		this.option.isMultiActive = isMultiActive;
		this.sinSeekBar.setEnable(isMultiActive);
		this.mSinSeekBar.setEnable(isMultiActive);
		this.cosSeekBar.setEnable(isMultiActive);
		this.sinSeekBar.setHideLabel(!isMultiActive);
		this.cosSeekBar.setHideLabel(!isMultiActive);
		this.mCosSeekBar.setEnable(isMultiActive);
		if(!isMultiActive)
		{
			this.setCosActive(this.option.isCosActive);

			if(!this.option.isCosActive)
				this.sinSeekBar.setHideLabel(true);
			else
				this.cosSeekBar.setHideLabel(true);
		}
		setHideHand(this.option.isHideHand);
		setHideCorner(this.option.isHideCorner);
		this.invalidate();
	}
	
	
	private boolean statusCorner = false;
	public void setShowDegree(boolean status)
	{
		this.statusCorner = status;
	}
	
	// ==========================================================
	// Constructor
	// ==========================================================
	// For xml layout
	public AnimationGameCanvas(Context context, AttributeSet attrs) {
		super(context, attrs);
		TypedArray a = context.obtainStyledAttributes(attrs,
				R.styleable.GameCanvas);
		this.option.isViewForAnimate = a.getBoolean(
				R.styleable.GameCanvas_is_animation_view, false);
		
		a.recycle();

		loadContent(context);
		Initialize();
	}

	// For activity ->setContentView
	public AnimationGameCanvas(Context context, boolean isViewForAnimate, boolean isFlipX) {
		super(context);

		this.option.isViewForAnimate = isViewForAnimate;
		this.option.isFlipX = isFlipX;
		loadContent(context);
		Initialize();
	}

	
	// ==========================================================
	// Process & Operand Method
	// ==========================================================
	private void calPos() {
		switch (this.option.align) {
		case ALIGN_CENTER:
			offset.x = this.option.position.x + ((doc_width - object_width) / 2);
			break;
		case ALIGN_LEFT:
			offset.x = this.option.position.x + this.sinSeekBar.getThumbWidth() / 2;
			break;
		case ALIGN_RIGHT:
			offset.x = this.option.position.x
					+ (doc_width - object_width - this.sinSeekBar
							.getThumbWidth() / 2);
			break;
		}
		switch (this.option.valign) {
		case VALIGN_MIDDLE:
			offset.y = this.option.position.y + ((doc_height - object_height) / 2);
			break;
		case VALIGN_TOP:
			offset.y = this.option.position.y + this.sinSeekBar.getThumbHeight() / 2;
			break;
		case VALIGN_BOTTOM:
			offset.y = this.option.position.y
					+ (doc_height - object_height - this.sinSeekBar
							.getThumbHeight() / 2);
			break;
		}
		int TRACKER_HEIGHT = (int)(50f*(((float)object_width)/320f));
		tanSeekBar.setPosition(offset.x, offset.y + object_width
				+ (int) ((float) (1f / 2f) * TRACKER_HEIGHT));
		cosSeekBar.setPosition(offset.x, offset.y + object_width
				+ (int) ((float) (3f / 2f) * TRACKER_HEIGHT));
		sinSeekBar.setPosition(offset.x, offset.y + object_width
				+ (int) ((float) (5f / 2f) * TRACKER_HEIGHT));

		mSinSeekBar.setPosition(offset.x, offset.y);
		mCosSeekBar.setPosition(offset.x, offset.y + object_width
				- (int) ((float) (1f / 2f) * TRACK_HEIGHT));

		int mWidth = (int)(mCosSeekBar.getProgress()*mCosSeekBar.getWidth());
		mWidth = (int)(mWidth>0.15f*object_width?0.15f*object_width:mWidth);
		int mHeight = (int)((mSinSeekBar.getProgress())*mSinSeekBar.getHeight());
		mHeight = (int)(mHeight>0.15f*object_width?0.15f*object_width:mHeight);
		
		cosRectF = new RectF(mCosSeekBar.getCursorX()-mWidth,  mCosSeekBar.getCursorY()-mWidth, mCosSeekBar.getCursorX()+mWidth, mCosSeekBar.getCursorY()+mWidth);
        sinRectF = new RectF(mSinSeekBar.getCursorX()-mHeight,  mSinSeekBar.getCursorY()-mHeight, mSinSeekBar.getCursorX()+mHeight, mSinSeekBar.getCursorY()+mHeight);
       
        if(corner_sin_anim!=null)corner_sin_anim.setForeground(new Rect(mSinSeekBar.getCursorX()-40,  mSinSeekBar.getCursorY()-60, mSinSeekBar.getCursorX()+60, mSinSeekBar.getCursorY()+40));
        if(corner_cos_anim!=null)corner_cos_anim.setForeground(new Rect(mCosSeekBar.getCursorX()-40,  mCosSeekBar.getCursorY()-60, mCosSeekBar.getCursorX()+60, mCosSeekBar.getCursorY()+40));
        
	}
	float aw = 0;
	private void calSize() {
		object_width = (int) (doc_height * this.option.scale);
		if(object_width>doc_width*0.8f)
			object_width = (int)(doc_width*0.8f);
		if (!this.option.isViewForAnimate)
			object_height = (doc_height > object_width + (TRACK_HEIGHT * 4)) ? object_width
					+ (TRACK_HEIGHT * 4)
					: doc_height;
		else
			object_height = object_width;

		cosSeekBar.setHeight(TRACK_HEIGHT);
		cosSeekBar.setWidth(object_width);

		sinSeekBar.setHeight(TRACK_HEIGHT);
		sinSeekBar.setWidth(object_width);

		tanSeekBar.setHeight(TRACK_HEIGHT);
		tanSeekBar.setWidth(object_width);

		mCosSeekBar.setHeight(TRACK_HEIGHT);
		mCosSeekBar.setWidth(object_width);

		mSinSeekBar.setHeight(object_width);
		mSinSeekBar.setWidth(object_width);

		aw = 0.0025f*object_width;
		anglePaint.setTextSize((((float)object_width)/200)*36);
		this.tanPaint.setStrokeWidth(aw*line_width);
		this.cosSeekBar.setLineWidth((int)(aw*line_width/2));
		this.sinSeekBar.setLineWidth((int)(aw*line_width/2));
		this.tanSeekBar.setLineWidth((int)(aw*line_width/2));
		this.mCosSeekBar.setLineWidth((int)(aw*line_width));
		this.mSinSeekBar.setLineWidth((int)(aw*line_width));
		//markAnglePaint.setStrokeWidth(aw*this.line_width);

		this.cosSeekBar.setSmoothScale(aw);
		this.sinSeekBar.setSmoothScale(aw);
		this.tanSeekBar.setSmoothScale(aw);
		
		calPos();
	}

	private void calProgress(LineBar obj, float progress,boolean saveData)
	{
		if (progress < 0 )
			progress = 0;
		if (progress > 1)
			progress = 1;
		if (obj == cosSeekBar) {
			if(saveData && this.option.isEnableCacheValue)
				UDataStorage.setStrData(this.getContext(), "cosSeekValue", progress
						+ "f");
			mCosSeekBar.setProgress(cosSeekBar.getProgress()*this.option.thumb_length);
			double Vl_w = Math.sin(Math.acos(progress)) * obj.getWidth();
			cosDegree = (float) ((180 / Math.PI) * (Math.acos(progress)));
			sinSeekBar.setProgress((float) (Vl_w / obj.getWidth()));
			mSinSeekBar.setProgress(sinSeekBar.getProgress()*this.option.thumb_length);
			if(saveData && this.option.isEnableCacheValue)
				UDataStorage.setStrData(this.getContext(), "sinSeekValue",
						sinSeekBar.getProgress() + "f");
		}
		if (obj == sinSeekBar) {
			if(saveData && this.option.isEnableCacheValue)
				UDataStorage.setStrData(this.getContext(), "sinSeekValue", progress
					+ "f");
			mSinSeekBar.setProgress(sinSeekBar.getProgress()*this.option.thumb_length);
			double Hl_h = Math.cos(Math.asin(progress)) * obj.getWidth();
			cosDegree = (float) ((180 / Math.PI) * (Math.asin(progress)));
			cosSeekBar.setProgress((float) (Hl_h / obj.getWidth()));
			mCosSeekBar.setProgress(cosSeekBar.getProgress()*this.option.thumb_length);
			if(saveData && this.option.isEnableCacheValue)
				UDataStorage.setStrData(this.getContext(), "cosSeekValue",
					cosSeekBar.getProgress() + "f");
		}
		if (obj == mCosSeekBar) {
			if(saveData && this.option.isEnableCacheValue)
				UDataStorage.setStrData(this.getContext(), "cosSeekValue", progress/this.option.thumb_length
					+ "f");
			cosSeekBar.setProgress(mCosSeekBar.getProgress()/this.option.thumb_length);
			
			double Vl_w = Math.sin(Math.acos(cosSeekBar.getProgress())) * obj.getWidth();
			cosDegree = (float) ((180 / Math.PI) * (Math.acos(cosSeekBar.getProgress())));
			sinSeekBar.setProgress((float) (Vl_w / obj.getWidth()));
			
			mSinSeekBar.setProgress(sinSeekBar.getProgress()*this.option.thumb_length);
			if(saveData && this.option.isEnableCacheValue)
				UDataStorage.setStrData(this.getContext(), "sinSeekValue",
					sinSeekBar.getProgress() + "f");
		}

		int mWidth = (int)(mCosSeekBar.getProgress()*mCosSeekBar.getWidth());
		mWidth = mWidth>30?30:mWidth;
		int mHeight = (int)(((obj == mSinSeekBar)?mSinSeekBar.getProgress(true):mSinSeekBar.getProgress())*mSinSeekBar.getHeight());
		mHeight = mHeight>30?30:mHeight;
		sinRectF = new RectF(mSinSeekBar.getCursorX()-mHeight,  mSinSeekBar.getCursorY()-mHeight, mSinSeekBar.getCursorX()+mHeight, mSinSeekBar.getCursorY()+mHeight);
        
		if (obj == mSinSeekBar) {
			if(saveData && this.option.isEnableCacheValue)
				UDataStorage.setStrData(this.getContext(), "sinSeekValue",
					mSinSeekBar.getProgress(true)/this.option.thumb_length + "f");
			sinSeekBar.setProgress(mSinSeekBar.getProgress(true)/this.option.thumb_length);
			mSinSeekBar.setProgress(sinSeekBar.getProgress()*this.option.thumb_length);
			
			double Hl_h = Math.cos(Math.asin(sinSeekBar.getProgress()))
					* obj.getWidth();
			cosDegree = (float) ((180 / Math.PI) * (Math.asin(sinSeekBar.getProgress())));
			cosSeekBar.setProgress((float) (Hl_h / obj.getWidth()));
			
			mCosSeekBar.setProgress(cosSeekBar.getProgress()*this.option.thumb_length);
			if(saveData && this.option.isEnableCacheValue)
				UDataStorage.setStrData(this.getContext(), "cosSeekValue",
					cosSeekBar.getProgress(true) + "f");

			mHeight = (int)(((obj == mSinSeekBar)?mSinSeekBar.getProgress():mSinSeekBar.getProgress())*mSinSeekBar.getHeight());
			mHeight = mHeight>30?30:mHeight;
			sinRectF = new RectF(mSinSeekBar.getCursorX()-mHeight,  mSinSeekBar.getCursorY()-mHeight, mSinSeekBar.getCursorX()+mHeight, mSinSeekBar.getCursorY()+mHeight);
	        
		}

		tanSeekBar.setProgress(1f);

		cosRectF = new RectF(mCosSeekBar.getCursorX()-mWidth,  mCosSeekBar.getCursorY()-mWidth, mCosSeekBar.getCursorX()+mWidth, mCosSeekBar.getCursorY()+mWidth);
        
        if(corner_sin_anim!=null)corner_sin_anim.setForeground(new Rect(mSinSeekBar.getCursorX()-40,  mSinSeekBar.getCursorY()-60, mSinSeekBar.getCursorX()+60, mSinSeekBar.getCursorY()+40));
        if(corner_cos_anim!=null)corner_cos_anim.setForeground(new Rect(mCosSeekBar.getCursorX()-40,  mCosSeekBar.getCursorY()-60, mCosSeekBar.getCursorX()+60, mCosSeekBar.getCursorY()+40));
        
	}
	
 	private void loadContent(Context context) {

		cosSeekBar = new LineBar(context, this, SeekBar.HORIZONTAL, false);
		sinSeekBar = new LineBar(context, this, SeekBar.HORIZONTAL, false);
		tanSeekBar = new LineBar(context, this, SeekBar.HORIZONTAL, false);

		mCosSeekBar = new LineBar(context, this, SeekBar.HORIZONTAL, true);
		mSinSeekBar = new LineBar(context, this, SeekBar.VERTICAL, true);

		tanPaint.setTextSize(25);
		tanPaint.setColor(0xff86c5f8);
		tanPaint.setStyle(Style.STROKE);
		tanPaint.setAntiAlias(true);
		tanPaint.setStrokeWidth(2);

        Typeface face = Typeface.createFromAsset(getContext().getAssets(), "fonts/THA0085.ttf");
        anglePaint.setTypeface(face);
		anglePaint.setTextSize(36);
		anglePaint.setARGB(255, 58, 58, 58);
		anglePaint.setStyle(Style.FILL);
		anglePaint.setAntiAlias(true);
		anglePaint.setStrokeWidth(1);

		markAnglePaint.setColor(Color.argb(255, 255, 200, 0));
		markAnglePaint.setStyle(Style.FILL_AND_STROKE);
		markAnglePaint.setAntiAlias(true);
		markAnglePaint.setStrokeJoin(Paint.Join.ROUND);
		markAnglePaint.setStrokeWidth(this.line_width);

		cosSeekBar.setTheme(SeekBar.THEME_GREEN_INVERT);
		sinSeekBar.setTheme(SeekBar.THEME_RED_INVERT);
		tanSeekBar.setTheme(SeekBar.THEME_BLUE);
		mCosSeekBar.setTheme(SeekBar.THEME_GREEN);
		mSinSeekBar.setTheme(SeekBar.THEME_RED);

		mCosSeekBar.setIsFlipX(this.option.isFlipX);
		mSinSeekBar.setIsFlipX(this.option.isFlipX);
	}

	// ==========================================================
	// Public Method
	// ==========================================================
	private static final int MODE_DISPLAY = 5;
	public static final int MODE_CAPTURE_FULL = 6;
	public static final int MODE_CAPTURE_SAMPLE = 7;

	public Bitmap saveSignature(int mode) {
		return saveSignature(mode, Color.TRANSPARENT);
	}
	
	@SuppressWarnings("finally")
	public Bitmap saveSignature(int mode ,int  backgroundColor) {
		GameCanvasOption restoreOption = this.option.clone();
		
		this.option.mode = (mode == MODE_CAPTURE_FULL || mode == MODE_CAPTURE_SAMPLE) ? mode
				: MODE_CAPTURE_FULL;
		this.option.isHideArc = true;
		this.setShowAngle(false);
		this.setHideCorner(true);
		this.setHideHand(true);
		
		Bitmap bitmap = null;
		try {
			bitmap = Bitmap.createBitmap(doc_width, doc_height,
					Bitmap.Config.ARGB_8888);

			bitmap.eraseColor(backgroundColor);
			draw(new Canvas(bitmap));

		} catch (OutOfMemoryError e) {
		} catch (Exception e) {
		}
		finally
		{
			this.option = restoreOption;
			this.option.isHideArc = this.option.isHideArc;
			this.setShowAngle(this.option.isShowAngle);
			this.setHideCorner(this.option.isHideCorner);
			this.setHideHand(this.option.isHideHand);
			return bitmap;
		}
	}

	public void Initialize() {
		offset = new Point(0, 0);

		calSize();

		cosSeekBar.setProgress(Float.parseFloat(UDataStorage.getStrData(
				this.getContext(), "cosSeekValue", "0f")));
		
		tanSeekBar.setEnable(false);
		tanSeekBar.setHideCursor(true);
		mSinSeekBar.setCursorDirection(SeekBar.CURSOR_DIRECTION_R2L);
		setCosActive(true);
		calProgress(cosSeekBar, cosSeekBar.getProgress(),true);
	}

	public void newStageWithAngle(boolean isCos, float angle) {
		angle = angle < 0.0f ? 0.0f : angle > 90.0f ? 90.0f : angle;
		if (isCos) {
			cosSeekBar.setProgress((float) (Math.cos(angle * Math.PI / 180)));
			calProgress(cosSeekBar, cosSeekBar.getProgress(),true);
		} else {
			cosSeekBar.setProgress((float) (Math.cos((90f-angle )* Math.PI / 180)));
			calProgress(cosSeekBar, cosSeekBar.getProgress(),true);
		}

	}

	public void newStageWithRedian(boolean isCos, float redian) {
		redian = redian < 0f ? 0f : redian > 1f ? 1f : redian;

		if (isCos) {
			cosSeekBar.setProgress(redian);
			calProgress(cosSeekBar, cosSeekBar.getProgress(),true);
		} else {
			sinSeekBar.setProgress(redian);
			calProgress(sinSeekBar, sinSeekBar.getProgress(),true);
		}
	}

	public float getSinAngle() {
		return 90f - cosDegree;
	}

	public float getCosAngle() {
		return cosDegree;
	}

	public float getSinRadian() {
		return this.sinSeekBar.getProgress();
	}

	public float getCosRadian() {
		return this.cosSeekBar.getProgress();
	}

	public void startFadeActive()
	{
		if(this.option.isCosActive)
		{
			this.mCosSeekBar.fadeActiveAnimation();
			this.cosSeekBar.fadeActiveAnimation();
		}
		else
		{
			this.mSinSeekBar.fadeActiveAnimation();
			this.sinSeekBar.fadeActiveAnimation();
		}
	}
	
	
	
	// ==========================================================
	// Event Method
	// ==========================================================

	public void onResume()
	{
		if (corner_cos_anim != null)corner_cos_anim.start();
		if (corner_sin_anim != null)corner_sin_anim.start();
	}

	public void onDestroy()
	{
		if (corner_cos_anim != null)corner_cos_anim.stop();
		if (corner_sin_anim != null)corner_sin_anim.stop();
	}

	public void onFinish()
	{
		if (corner_cos_anim != null)corner_cos_anim.finish();
		if (corner_sin_anim != null)corner_sin_anim.finish();
		if (cosSeekBar != null)cosSeekBar.finish(); 
		if (sinSeekBar != null)sinSeekBar.finish(); 
		if (tanSeekBar != null)tanSeekBar.finish();
		if (mCosSeekBar != null)mCosSeekBar.finish(); 
		if (mSinSeekBar != null)mSinSeekBar.finish();
		
	}
	
	@Override
	public void onSeekBarDragged(LineBar obj, float progress) {
		calProgress(obj, progress,true);
		if(!this.option.isViewForAnimate)
		{
			if(!this.option.isHideLabel && obj==mCosSeekBar)
				cosSeekBar.setCoverLabel(false);
			if(!this.option.isHideLabel && obj==mSinSeekBar)
				sinSeekBar.setCoverLabel(false);
		}
	}

	@Override
	public void onSeekBarDragging(LineBar obj, float progress) {
		calProgress(obj, progress,false);
	}

	@Override
	public void onSeekBarBeginDrag(LineBar obj, float progress) {
		if(!this.option.isViewForAnimate)
		{
			if(!this.option.isHideLabel && obj==mCosSeekBar)
				cosSeekBar.setCoverLabel(true);
			if(!this.option.isHideLabel && obj==mSinSeekBar)
				sinSeekBar.setCoverLabel(true);
		}
	}

	@SuppressLint("NewApi")
	@Override
	protected void onDraw(Canvas canvas) {

		if (!this.option.isViewForAnimate && this.option.isShowAngle) {
			canvas.drawText(getContext().getString(R.string.display_cos), offset.x + this.object_width
					- 100 - 22, offset.y + this.object_width - 20, anglePaint);
			canvas.drawText(String.format("% ."+this.option.num_float_bit+"f", cosDegree), offset.x + 32 + this.object_width
					- 100, offset.y + this.object_width - 20, anglePaint);
			canvas.drawText(getContext().getString(R.string.display_sin), offset.x + 10, offset.y + 20,
					anglePaint);
			canvas.drawText(String.format("% ."+this.option.num_float_bit+"f", (90f-cosDegree)), offset.x + 15 + 42, offset.y + 20,
					anglePaint);
		}

		boolean isDraw = !this.option.isViewForAnimate
				&& this.option.isShowSeekBar
				&& (this.option.mode == MODE_DISPLAY || this.option.mode == MODE_CAPTURE_FULL);
		
			if(!this.option.isHideArc)
				if(!this.option.multiCorner)
				{
					if(!this.option.isFlipX)
					{
				        if(this.option.isCosActive)
				        {
				        	canvas.drawArc (cosRectF, 180f,   cosDegree , true, markAnglePaint);
				       }
				        else
				        {
				        	canvas.drawArc (sinRectF,cosDegree, 90f - cosDegree, true, markAnglePaint);
				       }
					}
					else
					{
						 if(this.option.isCosActive)
						 {
					        canvas.drawArc (cosRectF,0f, -cosDegree , true, markAnglePaint);
				        }
					     else
					     {
					        canvas.drawArc (sinRectF,-180-cosDegree,-(90-cosDegree), true, markAnglePaint);
				        }
					}
			    }
				else
				{
					if(!this.option.isFlipX)
					{
						canvas.drawArc (cosRectF, 180f,   cosDegree , true, markAnglePaint);
			        
						canvas.drawArc (sinRectF,cosDegree, 90f - cosDegree, true, markAnglePaint);
				    }
					else
					{
			        	canvas.drawArc (cosRectF,0f, -cosDegree , true, markAnglePaint);
					 
						canvas.drawArc (sinRectF,-180-cosDegree,-(90-cosDegree), true, markAnglePaint);
					}
				}
		
		if (isDraw) 
		{
			cosSeekBar.drawLine(canvas);
			sinSeekBar.drawLine(canvas);
			tanSeekBar.drawLine(canvas);
		}

		mSinSeekBar.drawLine(canvas);
		mCosSeekBar.drawLine(canvas);

		canvas.drawLine(mCosSeekBar.getCursorX(), mCosSeekBar.getCursorY(),
				mSinSeekBar.getCursorX(), mSinSeekBar.getCursorY(), tanPaint);
		
		if (isDraw) {
			cosSeekBar.drawCursor(canvas);
			sinSeekBar.drawCursor(canvas);
			tanSeekBar.drawCursor(canvas);

		}

		mSinSeekBar.drawCursor(canvas);
		mCosSeekBar.drawCursor(canvas);

		if(statusCorner)
		if(!this.option.isHideArc)
			if(!this.option.multiCorner)
			{
				if(!this.option.isFlipX)
				{
			        if(this.option.isCosActive)
			        {
			        	String label = String.format("%.2f", cosDegree) + getContext().getString(R.string.degree_symbol);
						canvas.drawText(label,cosRectF.left - anglePaint.measureText(label)<cosSeekBar.getPosition().x?cosRectF.centerX()+5*aw:20>cosDegree?cosRectF.centerX()-anglePaint.measureText(label):cosRectF.left - anglePaint.measureText(label),cosDegree<20?cosRectF.bottom-cosRectF.height()/2 - 15*aw:cosRectF.bottom-cosRectF.height()/2 - 15*aw, anglePaint);
			        }
			        else
			        {
			        	String label = String.format("%.2f", (90f-cosDegree)) + getContext().getString(R.string.degree_symbol);
						canvas.drawText(label,sinRectF.left+sinRectF.width() ,90f - cosDegree>80?sinRectF.top  -sinRectF.height()/2:90f - cosDegree<30?sinRectF.top + sinRectF.height()/2:sinRectF.bottom, anglePaint);
			        }
				}
				else
				{
					 if(this.option.isCosActive)
					 {
			        	String label = String.format("%.2f", cosDegree) + getContext().getString(R.string.degree_symbol);
						canvas.drawText(label,15>cosDegree?cosRectF.left:cosRectF.right+anglePaint.measureText(label)>cosSeekBar.getPosition().x+cosSeekBar.getWidth()-2*aw?cosRectF.left  -anglePaint.measureText(label):cosRectF.right+5*aw,cosDegree<20?cosRectF.bottom-cosRectF.height()/2 - 15*aw:cosRectF.bottom-cosRectF.height()/2 - 5*aw, anglePaint);
					 }
				     else
				     {
			        	String label = String.format("%.2f", (90f-cosDegree)) + getContext().getString(R.string.degree_symbol);
						canvas.drawText(label,sinRectF.left-sinRectF.width()/2 ,90f - cosDegree>80?sinRectF.top +10*aw:90f - cosDegree<30?sinRectF.top + sinRectF.height()/2:sinRectF.bottom , anglePaint);
				     }
				}
		    }
			else
			{
				if(!this.option.isFlipX)
				{
					String label = String.format("%.2f", cosDegree) + getContext().getString(R.string.degree_symbol);
					canvas.drawText(label,cosRectF.left - anglePaint.measureText(label)<cosSeekBar.getPosition().x?cosRectF.centerX()+5:20>cosDegree?cosRectF.centerX()-anglePaint.measureText(label):cosRectF.left - anglePaint.measureText(label),cosDegree<20?cosRectF.bottom-cosRectF.height()/2 - 15*aw:cosRectF.bottom-cosRectF.height()/2 - 5*aw, anglePaint);
		        
		        	label = String.format("%.2f", (90f-cosDegree)) + getContext().getString(R.string.degree_symbol);
		        	canvas.drawText(label,sinRectF.left+sinRectF.width()+10*aw ,90f - cosDegree>80?sinRectF.top  -sinRectF.height()/2:90f - cosDegree<30?sinRectF.top + sinRectF.height()/2:sinRectF.bottom, anglePaint);
			    }
				else
				{
		        	String label = String.format("%.2f", cosDegree) + getContext().getString(R.string.degree_symbol);
					canvas.drawText(label,15>cosDegree?cosRectF.left:cosRectF.right+anglePaint.measureText(label)>cosSeekBar.getPosition().x+cosSeekBar.getWidth()-2*aw?cosRectF.left  -anglePaint.measureText(label):cosRectF.right+5*aw,cosDegree<20?cosRectF.bottom-cosRectF.height()/2 - 15*aw:cosRectF.bottom-cosRectF.height()/2 - 5*aw, anglePaint);
				 
		        	label = String.format("%.2f", (90f-cosDegree))+ getContext().getString(R.string.degree_symbol);
		        	canvas.drawText(label,sinRectF.left-sinRectF.width()/2 ,90f - cosDegree>80?sinRectF.top -sinRectF.height()/2:90f - cosDegree<30?sinRectF.top + sinRectF.height()/2:sinRectF.bottom , anglePaint);
				}
			}
		
		if(!this.option.isHideCorner)
		{
			if(this.option.multiCorner)
			{
				corner_cos_anim.Draw(canvas);
				corner_sin_anim.Draw(canvas);
			}
			else
			{
				if(this.option.isCosActive)
					corner_cos_anim.Draw(canvas);
				else
					corner_sin_anim.Draw(canvas);
			}
		}
		
		switch(this.option.pos_hand)
		{
			case POS_HAND_SLIDE:
				if(this.option.isMultiActive)
				{
					mSinSeekBar.drawHand(canvas);
					mCosSeekBar.drawHand(canvas);
				}
				else
				{
					if(!this.option.isCosActive)
						mSinSeekBar.drawHand(canvas);
					else
						mCosSeekBar.drawHand(canvas);
				}
				break;
			case POS_HAND_SEEKBAR:
				if(this.option.isMultiActive)
				{
					sinSeekBar.drawHand(canvas);
					cosSeekBar.drawHand(canvas);
				}
				else
				{
					if(!this.option.isCosActive)
						sinSeekBar.drawHand(canvas);
					else
						cosSeekBar.drawHand(canvas);
				}
				break;
			case POS_HAND_BOTH:
				if(this.option.isMultiActive)
				{
					sinSeekBar.drawHand(canvas);
					mSinSeekBar.drawHand(canvas);
					cosSeekBar.drawHand(canvas);
					mCosSeekBar.drawHand(canvas);
				}
				else
				{
					if(!this.option.isCosActive)
					{
						sinSeekBar.drawHand(canvas);
						mSinSeekBar.drawHand(canvas);
					}
					else
					{
						cosSeekBar.drawHand(canvas);
						mCosSeekBar.drawHand(canvas);
					}
				}
				break;
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		super.onTouchEvent(event);

		if (this.option.mode == MODE_DISPLAY) {
			// grab the pointer id
			int action = event.getAction();
			int pid = action >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
			int x = (int) event.getX(pid);
			int y = (int) event.getY(pid);
			
				if(!this.option.isHideCorner)
					return ((corner_sin_anim!=null&&corner_sin_anim.onTouch(action, x, y))
							|| (corner_cos_anim!=null&&corner_cos_anim.onTouch(action, x, y)));
		}
		return false;
	}

	@Override
	protected void onSizeChanged(int width, int height, int oldw, int oldh) {
		super.onSizeChanged(width, height, oldw, oldh);

		if (this.option.mode == MODE_DISPLAY && width != doc_width
				|| height != doc_height) {
			doc_width = width;
			doc_height = height;
			calSize();
		}
	}
	
	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		if (this.option.mode == MODE_DISPLAY && this.getMeasuredWidth() != doc_width
				|| this.getMeasuredHeight() != doc_height) {
			doc_width = this.getMeasuredWidth();
			doc_height = this.getMeasuredHeight();
			calSize();
		}
	}
	
	@Override
	public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		if (this.option.mode == MODE_DISPLAY && this.getMeasuredWidth() != doc_width
				|| this.getMeasuredHeight() != doc_height) {
			doc_width = this.getMeasuredWidth();
			doc_height = this.getMeasuredHeight();
			calSize();
		}

	}

	
}